#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include "SQLParser.h"
#include "parsersrc/util/sqlhelper.h"
#include <fcntl.h>
#include <unistd.h>
#include <cstring>
#include "stx/btree_set.h"
#include "index/Index.h"
#include "replacer/Replacer.h"

using namespace hsql;
/*--------------------------------------------------------------------------------------*/
struct DB{
/*--------------------------------------------------------------------------------------*/
#define PAGESIZE 4096
#define TABLE_HEADER_SIZE 512
#define MAX_COL_NUM 15
#define MAX_NAME_LENGTH 32  // 32 + 1, 1 is for '/0'
#define TABLE_HEADER_COL_NUMS 5
#define INT_LENGTH sizeof(int64_t)
#define BOOL_LENGTH 1
#define CHAR_LENGTH 255
#define SPLIT_LENGTH 1
#define TABLE_MAX_PAGE_NUM 3
#define PAGE_HEADER_SIZE 220
#define DB_MAX_TABLE_NUM 10
enum {
    UNABLE_OPEN_TABLE=-2, TABLE_IS_NOT_EXISTS=-3, DB_TABLE_BUFFER_IS_FULLED=-4, TABLE_IS_EXISTS = -5,
    TABLE_NOT_LOADED=-6, TABLE_LOADED =-7
}ERROR_CODE;

/*--------------------------------------------------------------------------------------*/
struct Row{
    char* buff[MAX_COL_NUM];
    Row() {
        for(int i = 0; i < MAX_COL_NUM; i++) {
            buff[i] = nullptr;
        }
    }
    ~Row() {
        for(int i = 0; i < MAX_COL_NUM; i++) {
            if(buff[i]) {
                free(buff[i]);
            }
        }
    }
};
struct Page{
    int pageId;  //4 byts
    int valueSize;
    int row_nums;
    int max_row_nums;
    bool dirty;//needn't to write disk
    std::vector<char> freeLocation;//200 bytes
    char* buffer;
    Page(){
        buffer = static_cast<char *>(malloc(sizeof(char) * (4096 - PAGE_HEADER_SIZE)));
        pageId = 0; row_nums = 0; valueSize = 0; max_row_nums = 0;
        dirty = false;
    }
    ~Page(){
        free(buffer);
    }
};
struct TableHeader{//maxSize 1024
    char* table_name;//bytes 32
    int col_nums;//byte 4
    char* col_name[MAX_COL_NUM];//bytes 32 * 15=480
    hsql::DataType col_type[MAX_COL_NUM];//bytes 15 * 1 = 15
    int colData_length[MAX_COL_NUM];//bytes 15 * 4 = 60
    int col_length[MAX_COL_NUM]; // bytes 15 *4 = 60
    int rowDataTotalSize;
    TableHeader() {
        for(int i = 0; i < MAX_COL_NUM; i++) {
            col_type[i] = DataType::UNKNOWN;
            col_length[i] = 0;
            col_name[i] = nullptr;
            colData_length[i] = 0;
        }
    }
    TableHeader(const TableHeader& obj) {
        *table_name = *obj.table_name;
        col_nums = obj.col_nums;
        *col_name = *obj.col_name;
        *col_type = *obj.col_type;
        *colData_length = *obj.colData_length;
        *col_length = *obj.col_length;
    }
    ~TableHeader() {
        if(col_name[0] != nullptr)
            freeAllocMemory();
    }

    void freeAllocMemory() {
        //because of init tableHeader and read bytes has used malloc to alloc memory
        free(table_name);
        for(int i = 0; i < col_nums; i++)
            free(col_name[i]);
    }
};
struct Table{
    int file_length; // update when table data file be modified
    TableHeader tableHeader;
    int file_descriptor;
    int header_deacriptor;
    Page* pager[TABLE_MAX_PAGE_NUM];
    Replacer replacers;
    Index index;
    Table(){
        replacers.maxCells = TABLE_MAX_PAGE_NUM;
        //init replacer
        for(int i = 0; i < TABLE_MAX_PAGE_NUM; i++)
            replacers.insertReplaceCell(i);
    }
    ~Table(){
    }
    void closeDataFile(){
        close(file_descriptor);
        //std::cout << "successful close data file function " << std::endl;
    }
    void closeHeaderFile(){
        close(header_deacriptor);
        //std::cout << "successful close data header file function " << std::endl;
    }
    void openDataFile(int dataFileDescriptor){//only open when db init
        //open table data file
        file_descriptor = dataFileDescriptor;
        file_length = lseek(file_descriptor, 0, SEEK_END);
        lseek(file_descriptor, 0, SEEK_SET);
        //std::cout << "successful open data file function " << std::endl;
    }
    void openHeaderFile(int headerFileDescriptor){
        header_deacriptor = headerFileDescriptor;
        lseek(header_deacriptor, 0, SEEK_END);
        //std::cout << "successful open header file function " << std::endl;
    }
    bool loadIndex(){
        if(!index.load(tableHeader.table_name, 0)){
            std::cout << "error index file is not exist" << std::endl;
            return -1;
        }
        return 0;
    }
    void storeIndex(){
        index.store(tableHeader.table_name, 0);
    }
    //table header no open data operator
    void readDetails(int fileDescriber){
        //lseek start location
        lseek(fileDescriber, 0, SEEK_SET);
        //read table name
        int tableNameLength = 0;
        read(fileDescriber, &tableNameLength, INT_LENGTH);
        tableHeader.table_name = static_cast<char *>(malloc(sizeof(char) * tableNameLength));
        ssize_t bytes_read = read(fileDescriber, tableHeader.table_name, tableNameLength);
        if(bytes_read == -1) {
            printf("read error\n");
            exit(EXIT_FAILURE);
        }
        //read table col numbers
        read(fileDescriber, &tableHeader.col_nums, INT_LENGTH);
        for(int i =0; i < tableHeader.col_nums; i++) {
            //read col name length
            read(fileDescriber, &tableHeader.col_length[i], INT_LENGTH);
            //read col name
            tableHeader.col_name[i] = static_cast<char *>(malloc(sizeof(char) * MAX_NAME_LENGTH * MAX_COL_NUM));
            read(fileDescriber, tableHeader.col_name[i], tableHeader.col_length[0]);
            //read col data type
            read(fileDescriber, &tableHeader.col_type[i], INT_LENGTH);
            //read col data offset
            read(fileDescriber, &tableHeader.colData_length[i], INT_LENGTH);
        }
        read(fileDescriber, &tableHeader.rowDataTotalSize, INT_LENGTH);
        //std::cout << "end read table header" << std::endl;
    }
    void storeDetail(int fileDescriber){
        //lseek start location
        off_t it = lseek(fileDescriber, 0, SEEK_SET);
        if(it == -1){
            std::cout << "close table header error " << std::endl;
            return;
        }
        //write table name length
        int NameLength = strlen(tableHeader.table_name);
        ssize_t bytes_write = write(fileDescriber, &NameLength, INT_LENGTH);
        //write table name
        bytes_write = write(fileDescriber, tableHeader.table_name, NameLength);
        if(bytes_write == -1) {
            printf("Error writing: %d\n", errno);
            exit(EXIT_FAILURE);
        }
        //write table col numbers
        write(fileDescriber, &tableHeader.col_nums, INT_LENGTH);
        //write table col details
        for(int i =0; i < tableHeader.col_nums; i++) {
            //write table col length
            write(fileDescriber, &tableHeader.col_length[i], INT_LENGTH);
            //write table col name
            write(fileDescriber, tableHeader.col_name[i], tableHeader.col_length[0]);
            //write table col dataType
            write(fileDescriber, &tableHeader.col_type[i], INT_LENGTH);
            //write table col data offset
            write(fileDescriber, &tableHeader.colData_length[i], INT_LENGTH);
        }
        write(fileDescriber, &tableHeader.rowDataTotalSize, INT_LENGTH);
        //std::cout << "end flush table header " << std::endl;
    }
    //page function no open data but modify table data
    void flushPage(int fileDescriber, int PageIndex){
        //lseek file
        int writeoff = pager[PageIndex]->pageId * PAGESIZE;
        off_t of = lseek(fileDescriber, writeoff, SEEK_SET);
        if(of == -1){
            std::cout << "off table page error " << std::endl;
            return;
        }
        //pageId
        write(fileDescriber, &(pager[PageIndex]->pageId), INT_LENGTH);
        //page
        write(fileDescriber, &(pager[PageIndex]->row_nums), INT_LENGTH);
        write(fileDescriber, &(pager[PageIndex]->max_row_nums), INT_LENGTH);
        write(fileDescriber, &(pager[PageIndex]->valueSize), INT_LENGTH);
        int freeSize = pager[PageIndex]->freeLocation.size();
        write(fileDescriber, &(freeSize), INT_LENGTH);
        for(int i = 0; i < pager[PageIndex]->freeLocation.size(); i++) {
            write(fileDescriber, &(pager[PageIndex]->freeLocation[i]), sizeof(pager[PageIndex]->freeLocation[i]));
        }
        lseek(fileDescriber, writeoff+PAGE_HEADER_SIZE, SEEK_SET);
        //write buffer
        write(fileDescriber, pager[PageIndex]->buffer, PAGESIZE - PAGE_HEADER_SIZE);
        //std::cout << "successful flush page" << std::endl;
        pager[PageIndex]->dirty = false;
        //update table_length
        int off = lseek(fileDescriber, 0, SEEK_END);
        file_length = off;
    }
    Row* getRow(int pageId, int pageRowOff){
        //check page is not exists
        int pageIndex = getPage(file_descriptor, pageId);
        if(pageIndex == -1){
            std::cout << "page id is not legal" << std::endl;
            exit(EXIT_FAILURE);
        }
        //std::cout << "page index is " << pageIndex << std::endl;
        //read row from page
        Row* readRow = new Row();
        readRow->buff[0] = static_cast<char *>(malloc(sizeof(char) * tableHeader.colData_length[0]));
        memcpy(readRow->buff[0], pager[pageIndex]->buffer + pageRowOff
               , tableHeader.colData_length[0]);
        for(int i = 1; i < tableHeader.col_nums; i++) {
            readRow->buff[i] = static_cast<char *>(malloc(sizeof(char) * tableHeader.colData_length[i]));
            memcpy(readRow->buff[i],
                   pager[pageIndex]->buffer + pageRowOff + tableHeader.colData_length[i-1],
                   tableHeader.colData_length[i]);
        }
//        for(int k = 0; k < tableHeader.col_nums; k++) {
//            switch (tableHeader.col_type[k]) {
//                case DataType::INT:
//                    std::cout<< *((int64_t*)readRow->buff[k]) <<" | ";
//                    break;
//                case DataType::VARCHAR:
//                    std::cout<< readRow->buff[k] << " | ";
//                    break;
//            }
//        }
//        std::cout << std::endl;
//        std::cout << "successful get row function" << std::endl;
        return readRow;
    }
    int getPage(int fileDescriber, int pageId){
        //check page id is he fa?
        int hasPages = file_length / PAGESIZE;
        //std::cout << "getPage has pages " << hasPages << std::endl;
        //std::cout << "search  pages "<< pageId << std::endl;
        if(pageId > hasPages) {
            return -1;
        }
        //check page is load?
        for(int i = 0; pager[i] && i < hasPages; i++) {
            if ( pager[i]->pageId == pageId) {
                return i;
            }
        }
        //page not load, replace table page buffer
        int replaceLocation = 0;
        replaceLocation = replacers.getReplaceCell();
        //std::cout << "replace location " << replaceLocation << std::endl;
        //
        if(pager[replaceLocation]) {
            //flush replaced page
            flushPage(fileDescriber,replaceLocation);
            delete pager[replaceLocation];
        }
        pager[replaceLocation] = new Page();
        //std::cout << "has id " << hasPages << std::endl;
        //read page from data file
        int off_t = pageId * PAGESIZE;
        //std::cout << "off = " << off_t << std::endl;
        int t = lseek(fileDescriber, off_t, SEEK_SET);
        //std::cout << t << std::endl;

        //pageId
        t = read(fileDescriber, &(pager[replaceLocation]->pageId), INT_LENGTH);
        //std::cout << t << std::endl;
        //row num
        t = read(fileDescriber, &(pager[replaceLocation]->row_nums),INT_LENGTH);
        //std::cout << t << std::endl;
        //max row num
        t = read(fileDescriber, &(pager[replaceLocation]->max_row_nums), INT_LENGTH);
        //std::cout << t << std::endl;
        //valueSize
        t = read(fileDescriber, &(pager[replaceLocation]->valueSize), INT_LENGTH);
        //std::cout << t << std::endl;
        //free location
        int readFreeSize = 0;
        t = read(fileDescriber, &readFreeSize, INT_LENGTH);
        //std::cout << t << std::endl;
        for(int i = 0; i < readFreeSize; i++){
            char location;
            read(fileDescriber, &location, sizeof(char));
            pager[replaceLocation]->freeLocation.push_back(location);
        }
        lseek(fileDescriber, off_t + PAGE_HEADER_SIZE, SEEK_SET);
        //buffer
        t = read(fileDescriber, pager[replaceLocation]->buffer, PAGESIZE - PAGE_HEADER_SIZE);
        //std::cout << t << std::endl;
        //std::cout << "end get page function " << std::endl;
        return replaceLocation;
    }
    int getFreePage(){
        int index = -1;
        int hasPages = file_length/PAGESIZE;
        if(file_length == 0){
            pager[0] = new Page();
            index = 0;
        } else {
            //flush buff
            for(int i = 0;pager[i] && i < TABLE_MAX_PAGE_NUM; i++) {
                if( pager[i]->dirty == true ){
                    flushPage(file_descriptor, i);
                }
            }
            //load page
            for(int i = 0; i < hasPages; i++){
                int t = getPage(file_descriptor, i);
                if(pager[t]->row_nums < pager[t]->max_row_nums) {
                    index = i;
                    break;
                }
            }
            if(index == -1){
                std::cout << "get free page error " << std::endl;
            }
            //std::cout << "page index " << index << std::endl;
        }
        //std::cout << "end free page f" <<std::endl;
        return index;
    }
    int getColumnId(char* columns){
        for(int i = 0; i < tableHeader.col_nums; i++) {
            if(strcmp(columns, tableHeader.col_name[i]) == 0) {
                return i;
            }
        }
        return -1;
    }
    void printRow(Row& readRow){
        for(int k = 0; k < tableHeader.col_nums; k++) {
            switch (tableHeader.col_type[k]) {
                case DataType::INT:
                    std::cout<< *((int64_t*)readRow.buff[k]) <<" | ";
                    break;
                case DataType::VARCHAR:
                    std::cout<< readRow.buff[k] << " | ";
                    break;
            }
        }
        std::cout << std::endl;
    }
    void insertRow(Row& row, int pageIndex, int pageRowOff){
        memcpy(pager[pageIndex]->buffer + pageRowOff
                , row.buff[0]
                , tableHeader.colData_length[0]);
        for(int i = 1 ; i < tableHeader.col_nums; i++) {
            memcpy(pager[pageIndex]->buffer + pageRowOff + tableHeader.colData_length[i - 1]
                    , row.buff[i],
                   tableHeader.colData_length[i]);
        }
        pager[pageIndex]->dirty = true;
    }

};


/*--------------------------------------------------------------------------------------*/
int getColOffset(ColumnType& columnType) {
    int length = 0;
    if (columnType.length != 0) {
        length =  columnType.length;
    } else {
        switch(columnType.data_type) {
            case DataType::INT:
                length = INT_LENGTH;
                break;
            default:
                length = MAX_NAME_LENGTH;
                break;
        }
    }
    return length;
}
hsql::DataType getDataType(DataType dataType) {
    hsql:DataType d = hsql::DataType::UNKNOWN;
    switch(dataType) {
        case hsql::DataType::CHAR:
            d = hsql::DataType::CHAR;
            std::cout << d << std::endl;
            break;
        case hsql::DataType::INT:
            d = hsql::DataType::INT;
            std::cout << d << std::endl;
            break;
        case hsql::DataType::VARCHAR:
            d = hsql::DataType::VARCHAR;
            std::cout << d << std::endl;
            break;
    }
    return d;
}
/*--------------------------------------------------------------------------------------*/
//table function
void printPage(Page& page){
    std::cout << "--------------------PAGE----------------------------" << std::endl;
    std::cout << page.pageId << std::endl;
    std::cout << page.row_nums << std::endl;
    std::cout << page.max_row_nums << std::endl;
    std::cout << page.valueSize << std::endl;
    std::cout << page.dirty << std::endl;
    std::cout << "-----------------PAGE------END----------------------" << std::endl;
}
int openTable(char* tableName) {
    int index = -1;
    //search free location
    for(int i = 0; i < DB_MAX_TABLE_NUM; i++){
        if(!tables[i]){
            index = i;
            break;
        }
    }
    if(index == -1) {
        return DB_TABLE_BUFFER_IS_FULLED;
    }
    tables[index] = new Table();
    //open header file
    std::string tableHeaderName(tableName);
    tableHeaderName.append("_header");
    int fd = open(tableHeaderName.c_str(),
                  O_RDWR ,  // create file if it does not exist
                  S_IWUSR |  //user write permission
                  S_IRUSR);  //user read permission
    if (fd == -1) {
        printf("Unable to open header file\n");
        return UNABLE_OPEN_TABLE;
    }
    tables[index]->openHeaderFile(fd);
    tables[index]->readDetails(tables[index]->header_deacriptor);
    int datafd = open(tableName,
                      O_RDWR ,  // create file if it does not exist
                      S_IWUSR |  //user write permission
                      S_IRUSR);  //user read permission
    if(datafd == -1){
        printf("Unable to open data file\n");
        return UNABLE_OPEN_TABLE;
    }
    tables[index]->openDataFile(datafd);
    //std::cout << "end open table function " << std::endl;
    return index;
}
void closeTable(int index){
    //std::cout << "close " << index << "table" <<std::endl;
    //flush tableHeader
    tables[index]->storeDetail(tables[index]->header_deacriptor);
    //flush tableData
    for(int i = 0; i < TABLE_MAX_PAGE_NUM; i++){
        if(tables[index]->pager[i] && tables[index]->pager[i]->dirty == true)
            tables[index]->flushPage(tables[index]->file_descriptor, i);
    }
    //free space
    for(int i = 0; i < TABLE_MAX_PAGE_NUM; i++) {
        if(tables[index]->pager[i])
            delete tables[index]->pager[i];
    }
    //close
    tables[index]->closeHeaderFile();
    tables[index]->closeDataFile();
}
int getTableInstance(char* tableName){
    //std::cout << "start get instance , table " << tableName << std::endl;
    int index = -1;//-1 is table is not exists
    int status = TABLE_NOT_LOADED;
    //check table is exists
    //std::cout << "check table is exists " << std::endl;
    for(int i = 0; i < hasTables.size(); i++){
        if(hasTables[i] && strcmp(hasTables[i], tableName) == 0){
            status = TABLE_IS_EXISTS;
            break;
        }
    }
    //std::cout << "end check table is exists " << std::endl;
    //table exists , check table is load
    //std::cout << "check table is load " << std::endl;
    if(status == TABLE_IS_EXISTS) {
        for(int i = 0; tables[i] && i < DB_MAX_TABLE_NUM; i++) {
            if( strcmp(tables[i]->tableHeader.table_name, tableName) == 0){
                status = TABLE_LOADED;
                index = i;
                break;
            }
        }
        //table is not load
        if(status != TABLE_LOADED){
            index = openTable(tableName);
        }
    }
    //std::cout << "end check table is load " << std::endl;
    //table is exists
    //std::cout << " end get instance" <<std::endl;
    return index;
}
/*--------------------------------------------------------------------------------------*/
int findElementInVector(char* val, std::vector<char*> resource) {
    for(int i = 0; i < resource.size(); i++) {
        if(strcmp(val, resource[i]) == 0) {
            return i;
        }
    }
    return -1;
}
int dealError(int error_code){
    switch(error_code){
        case UNABLE_OPEN_TABLE:
            return TABLE_IS_NOT_EXISTS;
        case DB_TABLE_BUFFER_IS_FULLED:
            return DB_TABLE_BUFFER_IS_FULLED;
        default:
            return error_code;
    }
}

/*--------------------------------------------------------------------------------------*/
//sql show function
void printTableDetails(TableHeader& tableHeader){
    std::cout << "--------------------------------------------------\n";
    std::cout << "table name : " << tableHeader.table_name << std::endl;
    std::cout << "table col nums : " << tableHeader.col_nums << std::endl;
    for(int i = 0; i < tableHeader.col_nums; i++) {
        std::cout << "---------------------columns------------------------\n";
        std::cout << "table col length  : " << tableHeader.col_length[i] << std::endl;
        std::cout << "table col name  : " << tableHeader.col_name[i] << std::endl;
        std::cout << "table col dataType  : " << tableHeader.col_type[i] << std::endl;
        std::cout << "table col data offset : " << tableHeader.colData_length[i] << std::endl;
    }
    std::cout << "table col data totalSize : " << tableHeader.rowDataTotalSize << std::endl;
    std::cout << "--------------------------------------------------\n";
}
void doShowTableColumns(char* tableName) {
    //open file
    int index = getTableInstance(tableName);
    if(index != -1){
        //std::cout << "start show function " << std::endl;
        //std::cout << tables[index]->tableHeader.table_name << std::endl;
        //the page 0 for table header
        printTableDetails(tables[index]->tableHeader);
    } else {
        std::cout << "error code: " << index << std::endl;
    }
    return;
}

/*--------------------------------------------------------------------------------------*/
//sql create function
void doCreateTable(const CreateStatement* stmt){
    //check whether this table  exists
    int index = getTableInstance(stmt->tableName);
    if(index != -1) {
        std::cout << "error code: " << TABLE_IS_EXISTS << std::endl;
        return;
    }
    //if not exists.
    //create table student (sid int, name varchar(20), age int);
    TableHeader tableHeader;
    //deal table name
    tableHeader.table_name = static_cast<char *>(malloc(sizeof(char)*strlen(stmt->tableName)));
    memcpy(tableHeader.table_name, stmt->tableName, strlen(stmt->tableName));
    //deal col numbers
    tableHeader.col_nums = (stmt->columns)->size();
    //deal column
    //std::cout << "jie duan 1" << std::endl;
    int totalSize = 0;
    for(int i = 0; i < tableHeader.col_nums; i++) {
        // [] > *
        auto& t = (*(*(stmt->columns))[i]);
        std::cout << "deal : " << t.name << std::endl;
        int col_size = strlen(t.name) + 1;
        tableHeader.col_length[i] = col_size;
        tableHeader.col_name[i] = static_cast<char *>(malloc(sizeof(char)*strlen(t.name)));
        strncpy(tableHeader.col_name[i], t.name, tableHeader.col_length[i]);
        tableHeader.col_type[i] = getDataType(t.type.data_type);
        tableHeader.colData_length[i] = getColOffset(t.type);
        totalSize += tableHeader.colData_length[i];
    }
    tableHeader.rowDataTotalSize = totalSize;

    //create header file
    std::string s(tableHeader.table_name);
    s.append("_header");
    int fd = open(s.data(),
                  O_RDWR | O_CREAT |
                  O_TRUNC,  // create file if it does not exist
                  S_IWUSR |  //user write permission
                  S_IRUSR);  //user read permission
    if (fd == -1) {
        printf("Unable to open file\n");
        exit(EXIT_FAILURE);
    }
    //write table name length
    int tableNameLength = strlen(tableHeader.table_name);
    ssize_t bytes_write = write(fd, &tableNameLength, INT_LENGTH);
    //write table name
    bytes_write = write(fd, tableHeader.table_name, tableNameLength);
    if(bytes_write == -1) {
        printf("Error writing: %d\n", errno);
        exit(EXIT_FAILURE);
    }
    //write table col numbers
    write(fd, &tableHeader.col_nums, INT_LENGTH);

    for(int i =0; i < tableHeader.col_nums; i++) {
        //write table col length
        write(fd, &tableHeader.col_length[i], INT_LENGTH);
        //write table col name
        write(fd, tableHeader.col_name[i], tableHeader.col_length[0]);
        //write table col dataType
        write(fd, &tableHeader.col_type[i], INT_LENGTH);
        //write table col data offset
        write(fd, &tableHeader.colData_length[i], INT_LENGTH);
    }
    write(fd, &tableHeader.rowDataTotalSize, INT_LENGTH);
    close(fd);
    //create table data file
    int datafile_fd = open(tableHeader.table_name,
                           O_RDWR | O_CREAT |
                           O_TRUNC,  // create file if it does not exist
                           S_IWUSR |  //user write permission
                           S_IRUSR);  //user read permission
    if (datafile_fd == -1) {
        printf("Unable to open file\n");
        exit(EXIT_FAILURE);
    }
    close(datafile_fd);
    hasTableNums++;
    char* name = new char[strlen(stmt->tableName)];
    strcpy(name, stmt->tableName);
    hasTables.push_back(name);
    for(int i = 0; i < hasTables.size(); i++) {
        if(hasTables[i] && strcmp(hasTables[i], stmt->tableName) == 0) {
            dbTableNameLenght[i] = strlen(stmt->tableName) + 1;
            break;
        }
    }
    flushDB(dbfile);
}


/*--------------------------------------------------------------------------------------*/
//sql insert function
void executeInsertStatement(const InsertStatement* stmt){
    switch(stmt->type) {
        case kInsertValues:
            doInsertValues(stmt);
            break;
    }
}
void doInsertValues(const InsertStatement* stmt) {
    //insert into student(sid,name,age) values(123, 'wang', 15);
    //get table
    int tableIndex = getTableInstance(stmt->tableName);//need to modify for we can't repeat open table;
    if(tableIndex == -1){
        std::cout << "error code : " << std::endl;
        return;
    }
    //std::cout << "table index" << tableIndex << std::endl;
    //create insert Row
    Row row;
    for(int i = 0; i < tables[tableIndex]->tableHeader.col_nums; i++) {
        row.buff[i] = static_cast<char *>(malloc(sizeof(char) * tables[tableIndex]->tableHeader.colData_length[i]));
        int index = findElementInVector(tables[tableIndex]->tableHeader.col_name[i], *(stmt->columns));
        //std::cout << "table name: " << tables[tableIndex]->tableHeader.col_name[i] << "--- insert name:" << (*(stmt->columns))[index] << std::endl;
        if (index == -1) {
            memset(row.buff[i], '!', sizeof(char*));
        } else {
            switch(stmt->values->at(index)->type) {
                case kExprLiteralInt: {
                    int64_t value = stmt->values->at(index)->ival;
                    //std::cout << value << std::endl;
                    memcpy(row.buff[i], &value, tables[tableIndex]->tableHeader.colData_length[i]);
                    break;
                }
                case kExprLiteralString:
                    //std::cout << stmt->values->at(index)->name << std::endl;
                    memcpy(row.buff[i], stmt->values->at(index)->name, tables[tableIndex]->tableHeader.colData_length[i]);
                    break;
            }
        }
    }
//    for(int i = 0; i < tables[tableIndex]->tableHeader.col_nums; i++) {
//        switch (tables[tableIndex]->tableHeader.col_type[i]) {
//            case DataType::INT:
//                std::cout<< *((int64_t*)row.buff[i]) <<std::endl;
//                break;
//            case DataType::VARCHAR:
//                std::cout<< row.buff[i] <<std::endl;
//                break;
//        }
//    }
    //std::cout << "temp row successful " << std::endl;

    //write to page
    //1.get page
    //data file is null
    int pageIndex = tables[tableIndex]->getFreePage();
    if (tables[pageIndex]->file_length == 0) {
        std::cout << "null page, page index "<< pageIndex <<std::endl;
        tables[tableIndex]->pager[pageIndex]->valueSize = tables[tableIndex]->tableHeader.rowDataTotalSize;
        tables[tableIndex]->pager[pageIndex]->max_row_nums
        = (PAGESIZE - PAGE_HEADER_SIZE) / tables[tableIndex]->tableHeader.rowDataTotalSize;
        //get inert location
        int pageRowOff = 0;
        //insert row to page
        memcpy(tables[tableIndex]->pager[pageIndex]->buffer + pageRowOff
               , row.buff[0]
               , tables[tableIndex]->tableHeader.colData_length[0]);
        for(int i = 1 ; i < tables[tableIndex]->tableHeader.col_nums; i++) {
            memcpy(tables[tableIndex]->pager[pageIndex]->buffer + pageRowOff + tables[tableIndex]->tableHeader.colData_length[i - 1]
                   , row.buff[i],
                   tables[tableIndex]->tableHeader.colData_length[i]);
        }
        tables[tableIndex]->pager[pageIndex]->freeLocation.push_back(0);
        tables[tableIndex]->pager[pageIndex]->row_nums++;
        tables[tableIndex]->pager[pageIndex]->dirty = true;
        //
        int64_t keyValue = *((int64_t*)row.buff[0]);
        IndexKey* indexKey = new IndexKey(keyValue, tables[tableIndex]->pager[pageIndex]->pageId, pageRowOff);
        tables[tableIndex]->index.insert(*indexKey);
        tables[tableIndex]->storeIndex();
    }
    else {
        //std::cout << "not null page" <<std::endl;
        //data file is not null
        //search location
        if(tables[tableIndex]->index.begin() == -1)
            tables[tableIndex]->loadIndex();
        int pageRowOff = 0;
        if(tables[tableIndex]->pager[pageIndex]->freeLocation.size() == tables[tableIndex]->pager[pageIndex]->max_row_nums) {
            for(int i = 0; i < tables[tableIndex]->pager[pageIndex]->freeLocation.size(); i++) {
                if(tables[tableIndex]->pager[pageIndex]->freeLocation[i] == 1) {
                    pageRowOff = i * tables[tableIndex]->pager[pageIndex]->valueSize;
                    break;
                }
            }
        } else {
            pageRowOff = tables[tableIndex]->pager[pageIndex]->row_nums
                    * tables[tableIndex]->pager[pageIndex]->valueSize;
        }
        int64_t keyValue = *((int64_t*)row.buff[0]);
        IndexKey* indexKey = new IndexKey(keyValue, tables[tableIndex]->pager[pageIndex]->pageId, pageRowOff);
        if(tables[tableIndex]->index.list.find(*indexKey)
        != tables[tableIndex]->index.list.end() ) {
            std::cout << "repeat content id" << std::endl;
            return;
        }
        tables[tableIndex]->index.insert(*indexKey);
        tables[tableIndex]->storeIndex();
        //std::cout << "pageRowoff " << pageRowOff << " \n";
        //3.insert
        memcpy(tables[tableIndex]->pager[pageIndex]->buffer + pageRowOff
               , row.buff[0]
               ,tables[tableIndex]->tableHeader.colData_length[0]);
        for(int i = 1 ; i < tables[tableIndex]->tableHeader.col_nums; i++)
            memcpy(tables[tableIndex]->pager[pageIndex]->buffer + pageRowOff + tables[tableIndex]->tableHeader.colData_length[i-1]
                   , row.buff[i], tables[tableIndex]->tableHeader.colData_length[i]);
        tables[tableIndex]->pager[pageIndex]->freeLocation.push_back(0);
        tables[tableIndex]->pager[pageIndex]->row_nums++;
        tables[tableIndex]->pager[pageIndex]->dirty = true;

    }
    //flush page
    tables[tableIndex]->flushPage(tables[tableIndex]->file_descriptor, pageIndex);
    //free space

}

/*--------------------------------------------------------------------------------------*/
//sql select function
void executeSelectStatement(const SelectStatement* stmt){
    int tableIndex = getTableInstance(stmt->fromTable->name);
    if(tableIndex == -1){
        std::cout << "error code , error : table is not exists" << std::endl;
        return;
    }
    int hasPageNums = tables[tableIndex]->file_length / PAGESIZE;
    //std::cout << "select " << tables[tableIndex]->file_length << " pages" << std::endl;
    //print
    std::cout << "----------------------------------------------------" << std::endl;
    //print col name
    for(int i = 0; i < tables[tableIndex]->tableHeader.col_nums; i++) {
        std:: cout << tables[tableIndex]->tableHeader.col_name[i] << " | ";
    }
    std::cout << std::endl;
    //print value
    std::cout << "----------------------------------------------------" << std::endl;
    if(hasPageNums > 0) {
        for(int i = 0; i < hasPageNums; i++){
            int pageIndex = tables[tableIndex]->getPage(tables[tableIndex]->file_descriptor, i);
            if(!tables[tableIndex]->pager[pageIndex] || tables[tableIndex]->pager[pageIndex]->row_nums == 0) continue;
            //printPage(*(tables[tableIndex]->pager[pageIndex]));
            //std::cout << "select " << tables[tableIndex]->pager[pageIndex]->row_nums << " rows" << std::endl;
            for(int j = 0; j < tables[tableIndex]->pager[pageIndex]->row_nums; j++) {
                Row* r = tables[tableIndex]->getRow(pageIndex
                       , j*tables[tableIndex]->tableHeader.rowDataTotalSize);
                tables[tableIndex]->printRow(*r);

                delete r;
            }

        }
    }
    std::cout << "----------------------------------------------------" << std::endl;

}

/*--------------------------------------------------------------------------------------*/
//sql delete function
void executeDeleteStatement(const DeleteStatement* stmt){
//    std::cout << stmt->tableName << std::endl;
//    std::cout << stmt->expr->expr->name << std::endl;  //delete col name
//    std::cout << stmt->expr->expr2->ival << std::endl; // delete col value
    char*s;
    switch(stmt->expr->expr2->type) {
        case kExprLiteralInt: {
            int64_t value = stmt->expr->expr2->ival;
            s = static_cast<char *>(malloc(sizeof(char) * INT_LENGTH));
            memcpy(s, &value, INT_LENGTH);
            break;
        }
        case kExprLiteralString:
            //std::cout << stmt->values->at(index)->name << std::endl;
            s = static_cast<char *>(malloc(sizeof(char) * CHAR_LENGTH));
            memcpy(s, stmt->expr->expr2->name, CHAR_LENGTH);
            break;
    }
    int tableIndex = getTableInstance(stmt->tableName);
    if(tableIndex == -1){
        std::cout << "error code , error : table is not exists" << std::endl;
        return;
    }
    int hasPageNums = tables[tableIndex]->file_length / PAGESIZE;
    int pageId = -1;
    int pageRowOff = -1;
    bool breakPoint = false;
    int rowIndex = -1;
    int pageIndex = 0;
    //check columns is exists
    for(int i = 0; i < tables[tableIndex]->tableHeader.col_nums; i++) {
        if(strcmp(stmt->expr->expr->name, tables[tableIndex]->tableHeader.col_name[i]) == 0) {
            rowIndex = i;
            break;
        }
    }
    //std::cout << "row index = " << rowIndex << std::endl;
    if (rowIndex == -1) {
        std::cout << "no this columns" << std::endl;
        return;
    }
    //check data file length
    if(hasPageNums > 0) {
        //search columns
        for(int i = 0; i < hasPageNums; i++){
            pageIndex = tables[tableIndex]->getPage(tables[tableIndex]->file_descriptor, i);
            for(int j = 0; j < tables[tableIndex]->pager[pageIndex]->row_nums; j++) {
                auto readRow =
                        tables[tableIndex]->getRow( i
                                , j*tables[tableIndex]->tableHeader.rowDataTotalSize);
                if(strcmp(readRow->buff[rowIndex], s) == 0) {
                    pageId = i;
                    pageRowOff = j;
                    breakPoint = true;
                }
                delete readRow;
            }
            if(breakPoint) break;
        }
        tables[tableIndex]->pager[pageIndex]->freeLocation[pageRowOff] = 1;
        tables[tableIndex]->pager[pageIndex]->row_nums--;
        tables[tableIndex]->pager[pageIndex]->dirty = true;
        memcpy(tables[tableIndex]->pager[pageIndex]->buffer + pageRowOff * tables[tableIndex]->tableHeader.rowDataTotalSize
                , tables[tableIndex]->pager[pageIndex]->buffer + (pageRowOff+1) * tables[tableIndex]->tableHeader.rowDataTotalSize
                , PAGESIZE - PAGE_HEADER_SIZE - (pageRowOff+1) * tables[tableIndex]->tableHeader.rowDataTotalSize);
        tables[tableIndex]->flushPage(tables[tableIndex]->file_descriptor,pageIndex);
    }
    free(s);
    //std::cout << "page id is: " << pageId << std::endl;
    //std::cout << "page row off is : " << pageRowOff << std::endl;
}

/*--------------------------------------------------------------------------------------*/
//sql update function
//get page id and row off update
void executeUpdateStatement(const UpdateStatement* stmt){
    //get where option
    char*s;
    switch(stmt->where->expr2->type) {
        case kExprLiteralInt: {
            int64_t value = stmt->where->expr2->ival;
            s = static_cast<char *>(malloc(sizeof(char) * INT_LENGTH));
            memcpy(s, &value, INT_LENGTH);
            std::cout << value << std::endl;
            break;
        }
        case kExprLiteralString:
            s = static_cast<char *>(malloc(sizeof(char) * CHAR_LENGTH));
            memcpy(s, stmt->where->expr2->name, CHAR_LENGTH);
            std::cout << s << std::endl;
            break;
    }
    //get table instance
    int tableIndex = getTableInstance(stmt->table->name);
    if(tableIndex == -1){
        std::cout << "error code , error : table is not exists" << std::endl;
        return;
    }
    //search row get page id and page row off
    int hasPageNums = tables[tableIndex]->file_length / PAGESIZE;
    std::vector<int> pageIds;
    std::vector<int> pageRowOffs;
    std::vector<int> updateSort;
    bool errStatus = false;
    int rowIndex = -1;
    int pageIndex = 0;
    //check columns is exists
    //check update col
    for(int i = 0; i < stmt->updates->size(); i++) {
        int id = tables[tableIndex]->getColumnId( (*(stmt->updates))[i]->column);
        if(id == -1) {
            std::cout << "error code, error: update columns is not exists" << std::endl;
            free(s);
            return;
        }
        updateSort.push_back(id);
    }
    //check where col
    rowIndex = tables[tableIndex]->getColumnId(stmt->where->expr->name);
    if(rowIndex == -1) {
        std::cout << "error code, error: where columns is not exists" << std::endl;
        free(s);
        return;
    }
    //std::cout << "row index = " << rowIndex << std::endl;

    //check data file length
    if(hasPageNums > 0) {
        //search columns
        for(int i = 0; i < hasPageNums; i++){
            pageIndex = tables[tableIndex]->getPage(tables[tableIndex]->file_descriptor, i);
            for(int j = 0; j < tables[tableIndex]->pager[pageIndex]->row_nums; j++) {
                auto readRow =
                        tables[tableIndex]->getRow( i
                                , j*tables[tableIndex]->tableHeader.rowDataTotalSize);
                if(strcmp(readRow->buff[rowIndex], s) == 0) {
                    //find successful
                    pageIds.push_back(i);
                    pageRowOffs.push_back(j);
                }
                delete readRow;
            }
        }
        //update
        for(int i = 0; i < pageIds.size(); i++) {
            int needPageId = pageIds[i];
            int needPageRowOff = pageRowOffs[i];
            //std::cout << "need page id " << needPageId << ", need row off " << needPageRowOff << std::endl;
            auto tempRow = tables[tableIndex]->getRow(needPageId
                    , needPageRowOff * tables[tableIndex]->tableHeader.rowDataTotalSize);
            //update tempRow
            for(int j = 0; j < updateSort.size(); j++){
                int needColIndex = updateSort[j];
                switch(tables[tableIndex]->tableHeader.col_type[needColIndex]) {
                    case DataType::INT:{
                        int64_t value = (*(stmt->updates))[j]->value->ival;
                        memcpy(tempRow->buff[needColIndex], &value, INT_LENGTH);
                        break;
                    }
                    case DataType::VARCHAR:
                        memcpy(tempRow->buff[needColIndex]
                               , (*(stmt->updates))[j]->value->name
                               , tables[tableIndex]->tableHeader.colData_length[needColIndex]);
                        break;
                }
            }
            //update page
            tables[tableIndex]->insertRow(*tempRow
                                          , needPageId
                                          , needPageRowOff * tables[tableIndex]->tableHeader.rowDataTotalSize);
            //
            delete tempRow;
        }
        tables[tableIndex]->flushPage(tables[tableIndex]->file_descriptor,pageIndex);
    } else {
        std::cout << "error code , error : table is null" << std::endl;
    }
    free(s);
}

/*--------------------------------------------------------------------------------------*/
//sql execute function
void executeShowStatement(const ShowStatement* stmt) {
    switch(stmt->type) {
        case kShowColumns:
            doShowTableColumns(stmt->name);
            break;
        default:
            break;
    }
}
void executeCreateStatement(const CreateStatement* stmt){
    //how to define the column length
    switch (stmt->type) {
        case kCreateTable:
            doCreateTable(stmt);
            break;
//        case kCreateIndex:
//            doCreateIndex(stmt);
//            break;
    }
}
void executeStatement(const SQLStatement* stmt) {
    switch (stmt->type()) {
        case kStmtSelect:
            executeSelectStatement((const SelectStatement*)stmt);
            break;
        case kStmtCreate:
            executeCreateStatement((const CreateStatement*)stmt);
            break;
        case kStmtShow:
            executeShowStatement((const ShowStatement*)stmt);
            break;
        case hsql::kStmtInsert:
            executeInsertStatement((const InsertStatement*)stmt);
            break;
        case hsql::kStmtDelete:
            executeDeleteStatement((const DeleteStatement*)stmt);
            break;
        case hsql::kStmtUpdate:
            executeUpdateStatement((const UpdateStatement*)stmt);
            break;
        default:
            break;
    }
}


/*--------------------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------------------*/
DB(){
    hasTableNums = 0;
    dbName = "hello";
    hasIndexNums = 0;
    for(int i =0; i < DB_MAX_TABLE_NUM; i++){
        tables[i] = nullptr;
        hasIndexes[i] = nullptr;
        dbTableNameLenght[i] = 0;
    }
}
void init(){
    std::string s(dbName);
    s.append(".db");
    int fd = open(s.c_str(),
                  O_RDWR | O_CREAT,  // create file if it does not exist
                  S_IWUSR |  //user write permission
                  S_IRUSR);  //user read permission
    if (fd == -1) {
        printf("Unable to open file\n");
        exit(EXIT_FAILURE);
    }
    lseek(fd, 0, SEEK_SET);
    write(fd, &hasIndexNums, INT_LENGTH);
    write(fd, &hasTableNums, INT_LENGTH);
    dbfile = fd;
}
void initFromDisk(int file){
    lseek(file, 0, SEEK_SET);
    read(file, &hasIndexNums, INT_LENGTH);
    read(file, &hasTableNums, INT_LENGTH);
    for(int i = 0; i < hasTableNums; i++) {
        read(file, &dbTableNameLenght[i], INT_LENGTH);
        char*s = new char[dbTableNameLenght[i]];
        read(file, s, dbTableNameLenght[i]);
        hasTables.push_back(s);
    }
    dbfile = file;
}
void flushDB(int file){
    if(file == -1) {
        std::cout << "db data file error " << std::endl;
        return;
    }
    std::cout << "db flush start " << std::endl;
    off_t of = lseek(file, 0, SEEK_SET);
    if(of == -1){
        std::cout << "off db data file error " << std::endl;
        return;
    }
    write(file, &hasIndexNums, INT_LENGTH);
    write(file, &hasTableNums, INT_LENGTH);
    for(int i = 0; dbTableNameLenght[i] != 0 && hasTables[i]  && i < DB_MAX_TABLE_NUM; i++) {
        write(file, &dbTableNameLenght[i], INT_LENGTH);
        write(file, hasTables[i], dbTableNameLenght[i]);
    }
    std::cout << "db flush successful " << std::endl;
}
void closeDB(){
    flushDB(dbfile);
    //close table
    for(int i = 0; i < DB_MAX_TABLE_NUM; i++){
        if(tables[i]) {
            closeTable(i);
            delete tables[i];
        }
    }
    //close index
    for(int i = 0; i < DB_MAX_TABLE_NUM; i++){
        if(hasIndexes[i])
            delete hasIndexes[i];
    }
    close(dbfile);
}
void printDB(){
    std::cout << "--------------------------------------------------\n";
    std::cout << "has " << hasTableNums << std::endl;
    std::cout << "--------------------------------------------------\n";
    for(int i = 0; i < hasTableNums && hasTables[i]; i++){
        std::cout << "| " << hasTables[i] << " | "
        << dbTableNameLenght[i] << std::endl;
    }
    std::cout << "--------------------------------------------------\n";
}
/*--------------------------------------------------------------------------------------*/
std::vector<char*> hasTables;
int hasTableNums;
int hasIndexNums;
char* dbName;
Index* hasIndexes[DB_MAX_TABLE_NUM];
Table* tables[DB_MAX_TABLE_NUM];
int dbTableNameLenght[DB_MAX_TABLE_NUM];
int dbfile;
};



/*--------------------------------------------------------------------------------------*/

/*-----------------------------IO---------------------------------------------------------*/
struct InputBuffer{
    char* str;
    std::string stri;
    size_t str_length;
    ssize_t input_length;
public:
    InputBuffer() :
            str(nullptr), str_length(0), input_length(0){}
    ~InputBuffer(){}
    void readInput(){
        std::getline(std::cin, stri);
    }
};
/*---------------------------Prompt-----------------------------------------------------------*/
void print_prompt() {
    std::cout << "db > " << std::flush;
}
/*--------------------------------------------------------------------------------------*/
//create table student (sid int, name varchar(20), age int);
//insert into student(sid,name,age) values(123, 'wang', 15);
//update student set age = 25 where sid = 123;
//delete from student where sid = 123;

/*--------------------------------------------------------------------------------------*/
int main(){
    //init database;read db file for read table list of this db
    InputBuffer inputBuffer{};
    DB* db = new DB();
    std::string s(db->dbName);
    s.append(".db");
    int dbfd = open(s.data(),
                  O_RDWR,  // create file if it does not exist
                  S_IWUSR |  //user write permission
                  S_IRUSR);  //user read permission
    if (dbfd == -1) {
        //init db
        db->init();
    } else {
        //init from disk
        db->initFromDisk(dbfd);
    }
    std::cout << "init db successful " << std::endl;
    while(true) {
        //read input
        print_prompt();
        inputBuffer.readInput();
        //std::cout << inputBuffer.stri << std::endl;
        if (inputBuffer.stri[0] == '.') {//do meta command
            if (inputBuffer.stri == ".exit") {
                //flush db
                db->closeDB();
                break;
            }
            else if(inputBuffer.stri == ".commit") {
                //flush table
            } else if(inputBuffer.stri == ".print"){
                db->printDB();
            }
        }
        else {//do sql command
            std::string query(inputBuffer.stri);
            // parse a given query
            hsql::SQLParserResult result;
            hsql::SQLParser::parse(query, &result);

            // check whether the parsing was successful
            if (result.isValid()) {
                printf("Parsed successfully!\n");
                printf("Number of statements: %lu\n", result.size());

                for (auto i = 0u; i < result.size(); ++i) {
                    // Print a statement summary.
                    //hsql::printStatementInfo(result.getStatement(i));
                    db->executeStatement(result.getStatement(i));
                }
            } else {
                fprintf(stderr, "Given string is not a valid SQL query.\n");
                fprintf(stderr, "%s (L%d:%d)\n",
                        result.errorMsg(),
                        result.errorLine(),
                        result.errorColumn());
                return -1;
            }
        }
    }
    //flush db
    exit(EXIT_SUCCESS);
}